from rest_framework import serializers
from django.db import transaction
from django.utils import timezone
from django_redis import get_redis_connection
from decimal import Decimal
from goods.models import SKU
from .models import OrderInfo, OrderGoods
import logging

# 獲取日志操作對象
logger = logging.getLogger("django")


class CartSKUserialzers(serializers.ModelSerializer):
    """
    購物車商品數據序列化器
    """
    count = serializers.IntegerField(label="數量")

    class Meta:
        model = SKU

        fields = ("id", "name", "default_image_url", "price", "count")


class OrederSettlementSerialzier(serializers.Serializer):
    """
    訂單結算數據序列化器
    """
    freight = serializers.DecimalField(label="運費", max_digits=10, decimal_places=2)

    skus = CartSKUserialzers(many=True)


class SaveOderSerialiuzer(serializers.ModelSerializer):
    """
    下單數據序列化器
    """

    class Meta:
        model = OrderInfo
        # 只讀指段列表
        fields = ("order_id", "address", "pay_method")
        read_only_fields = ("order_id")

        extra_kwargs = {
            "address": {
                "write_only": True,
                "required": True,
            },
            "pay_method": {
                "write_only": True,
                "required": True
            }
        }

    def create(self, validated_data):
        """保存訂單"""
        # 功能需求分析
        """
        1、獲取當前下單用戶
        ２、生成訂單編號
        ３、保存訂單基本信息數據Oreder Info
        ４、從購物車中獲取結算商品
        ５、遍歷結算商品
            ！、判斷商品庫存是否充足
            ２、減少商品庫存，增加商品數量
            ３、保存訂單商品數據
        ６、在ｒｅdis購物車中刪除以計算商品數據
        """
        # 獲取當前下單用戶
        user = self.context["request"].user

        # 組織訂單編號
        order_id = timezone.now().strftime("%Y%m%d%H%M%S") + ("%09d" % user.id)

        # 保存訂單基本信息數據　Oreder Info
        # 獲取地址
        address = validated_data["address"]
        # 獲取支付方式
        pay_method = validated_data["pay_method"]

        """
        transaction.atomic()表示把with縮進中的代碼段裏面的ｍｙｓｑｌ數據庫所有操作作爲一個事物
        注意：
        數據庫事物功能只針對mysql數據庫，對於序列化器，django　，ｒｅｄｉｓ不起任何作用

        """
        # 生成訂單
        with transaction.atomic():
            # 設置事物保存點，在操作失敗的時候或者校驗失敗的時候回滾到這裏
            save_id = transaction.savepoint()
            try:
                # 創建訂單信息
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,  # 訂單商品總數
                    total_amount=Decimal(0),  # 訂單總金額,縣默認爲０
                    freight=Decimal(10),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method == OrderInfo.PAY_METHODS_ENUM[
                        'CASH'] else
                    OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                )

                # 獲取購物車信息
                redis_conn = get_redis_connection("cart")
                redis_cart = redis_conn.hgetall("cart_%s" % user.id)
                cart_selected = redis_conn.smembers("cart_selected_%s" % user.id)

                # 將ｂｙｅｓ數據轉爲ｉｎｔ型
                cart = {}

                for sku_id in cart_selected:
                    cart[int(sku_id)] = int(redis_cart[sku_id])

                # 便利結算商品
                for sku_id in cart:
                    while True:
                        # 獲取當前對應sku_id信息
                        sku = SKU.objects.get(pk=sku_id)
                        # 本次購買商品的數量
                        sku_count = cart[sku.id]
                        print("sku_count_%s" % sku_count)

                        # 判斷庫存
                        origin_stock = sku.stock  # 原始庫存
                        origin_sales = sku.sales  # 原始銷量

                        # 判斷商品庫存是否充足
                        if sku_count > origin_stock:
                            # 如果商品不足回滾到保存點
                            transaction.savepoint_rollback(save_id)
                            raise serializers.ValidationError("商品庫存不足")

                        # 用於下單演示
                        import time
                        time.sleep(5)

                        # 5.2 减少商品库存，增加商品销量[sku单品销量、SPU总销量]
                        # sku.stock = origin_stock - sku_count # 新库存
                        # sku.sales = origin_sales + sku_count # 新销量
                        # sku.save()

                        # 調整代碼，使用樂觀鎖

                        new_stock = origin_stock - sku_count
                        new_sales = origin_sales + sku_count

                        # 使用樂觀鎖的步奏
                        # 1、縣查詢原始記錄，保存原始記錄值
                        # 2、在跟新的時候，把原始值作爲條件
                        ret = SKU.objects.filter(id=sku.id, stock=origin_stock).update(stock=new_stock, sales=new_sales)

                        # 判斷是否更新陳工，如果不成功，就繼續循環直到保存成功
                        # 在樂觀鎖中並沒有保存成功，那麼就讓用戶重新判斷商品庫存

                        if ret == 0:
                            continue

                        # ret 的值不是０的，則表示當前商品ｓｋｕ庫存充足，也就是更新成功執行後面的代碼

                        break

                    # 累計商品的ｓｐｕ銷量信息
                    sku.goods.sales += sku_count
                    sku.goods.save()

                    # 累計訂單基本信息數據
                    order.total_count += sku_count  # 累計本次訂單的商品數量
                    order.total_amount += (sku.price * sku_count)

                    # 保存當前訂單的商品數據
                    OrderGoods.objects.create(
                        order=order,
                        sku=sku,
                        count=sku_count,
                        price=sku.price
                    )

                # 更新訂單的金額數量信息
                order.total_amount += order.freight

                order.save()
            except serializers.ValidationError:
                # 這裏表示是序列化器，而校驗錯誤拋出異常，是要給視圖處理，返回給前端
                # 所以這裏我們不做任何處理，直接使用raise ,可以把當前錯誤網視圖拋

                raise


            except Exception as e:
                logger.error(e)
                # 如果出現別的異常，也要回滾視圖，取消ｗhile 語句中的ｍｙｓｑｌ操作

                transaction.savepoint_rollback(save_id)
                raise

                # 提交事物

            transaction.savepoint_commit(save_id)

            # 刪除購物車中的以計算的商品


            p1 = redis_conn.pipeline()
            p1.hdel("cart_%s" % user.id, *cart_selected)
            p1.srem("cart_selected_%s" % user.id, *cart_selected)
            p1.execute()
            return order


